/*
 * Copyright (c) 2009 Nathaniel Houghton <nathan@brainwerk.org>
 *                    Robert Kirchgessner <rkirchge@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for
 * any purpose with or without fee is hereby granted, provided that
 * the above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "elm.h" 
#include "obd_pid.h"
#include "elm_dtc.h"

static struct dtc_head 	head; /*The first DTC code*/
static struct dtc 		*curr; /*Current DTC pointer*/

uint16_t elm_dtc_get( uint8_t reset ); /*Get the actual DTCs*/

uint8_t 	
elm_dtc_peek() /*Gets the number of trouble codes detected, and sets check_engine to 1 if the check enchne light is on*/
{
	uint8_t buf[4]; 
	
	printf("Give me the MILDTC (4 bytes)\n");
	elm_getpid( STATUS_MILDTC, buf, sizeof(buf) ); /*Get the MIL/DTC code*/
	
	return buf[0]; /*Return the first byte. MSB is the check engine light flag. The rest is the #DTC codes.*/
}

void
elm_dtc_update( uint8_t peek_resp ) /*Get new DTC list from the car*/
{
	char buf[] = "03\r"; /*Mode 3 gets the trouble codes*/
	
	uint8_t i;
	uint8_t num_dtc; /*Current number of DTC*/
	int8_t create_cnt; /*Number of DTC nodes we need to delete/create.*/
	
	struct dtc *tmp;
	
	num_dtc = DTC_COUNT_MSK & peek_resp; /*Get the current number of DTC*/

	if( head.last == NULL)
		head.last = (struct dtc*)&head;
	
	if(head.next == NULL)
		head.next = (struct dtc*)&head;
	
	if(head.size == 0)
		head.size = 1;
	
	if(num_dtc == 0)
	{
		printf("Deleting all nodes...\n");
		tmp = head.last->prev;

		for(i=0; i<head.size; ++i)
		{
			free(tmp->next);
			tmp = tmp->prev;
		}
	
		head.size = 1;

		return;
	}	

	elm_dtc_get(1); /*Reset the counter, doesnt grab any data.*/
	
	create_cnt = num_dtc - head.size; /*Find the number of DTC needed to be created/deleted.*/
	
	printf("Create counter: %d\n", create_cnt);
	while( create_cnt > 0 ) /*While we need to create more nodes*/
	{
		printf("Making a new node\n");
		tmp = malloc( sizeof( struct dtc ) ); /*Allocate a new DTC node*/
		
		if( head.last != NULL ) /*If there is a last node*/
		{
			printf("Node detected already\n");
			tmp->prev = head.last; /*Set the previous node to the old last*/
			head.last->next = tmp; /*Create the new last node*/
		} 
		else
		{
			printf("No previous node, setting previous equal to head node.\n");
			tmp->prev = (struct dtc*)&head; /*Set the previous as the head*/
			head.next = tmp; /*Set the next node*/
		}

		head.last = tmp; /*Set the new last node*/
		head.last->next = (struct dtc*)&head; /*Set the last nodes next to the head node.*/
		
		create_cnt--;
	}
	
	while( create_cnt < 0 ) /*While we need to delete nodes.*/
	{
		printf("Deleting some nodes\n");
		
		tmp = head.last->prev; /*Get the previous node.*/
		
		if( head.last != NULL && head.last != (struct dtc*)&head ) /*Dont delete the head*/
			free( head.last ); /*Delete the old node*/
		else 
			break; /*If we hit the head node, might as well stop*/
		
		head.last = tmp; 
		head.last->next = (struct dtc*)&head;
		
		create_cnt++;
	}

	printf("Give me the mode 3 DTC lines 6 bytes per line\n");
	
	tmp = (struct dtc*)&head; /*Set the starting node*/
	usart_put( DEV_ELM, buf, sizeof(buf) ); /*Send the MODE03 request to get the DTC*/
	
	printf("num_dtc %d calling elm_dtc_get\n", num_dtc);
	
	for( i=0; i<num_dtc; ++i )
	{
		tmp->code = elm_dtc_get(0); /*Get the next DTC*/
		tmp = tmp->next;
	}

	head.size = num_dtc;
	curr = (struct dtc*) &head; /*Update the current node to the head.*/

	elm_flush('>');
	
	return;
}

uint16_t
elm_dtc_next() /*Get the next dtc*/
{
	uint16_t code;
	code = curr->code;
	curr = curr->next;
	
	return code;
}

uint16_t
elm_dtc_prev() /*Get previous dtc*/
{
	uint16_t code;
	code = curr->code;
	curr = curr->prev;
	
	return code;
}

void	  	
elm_dtc_clear() /*Clear the trouble codes*/
{
	uint8_t buf[] = "04\r";

	/*Need to do some cleanup for the DRC structure here */
	
	/*Clearing the trouble codes is as easy as sending a MODE04 request*/ 
	usart_put( DEV_ELM, buf, sizeof(buf) ); /*Send the MODE04 request to clear the DTC*/

	elm_flush('>'); /*Find the prompt*/

}

uint16_t 
elm_dtc_get( uint8_t reset )  /*Get the actual DTCs*/
{
	uint8_t buf[2];
	static uint8_t cnt; /*Keep track of how many DTC we read, so we know when to expect the next tag.*/

	if( reset == 1 )
	{
		cnt = 0;
		return 0;
	}
	
	printf("elm_dtc_get getting data..\n");
	
	if(cnt == 0 )
		elm_readdata( buf, 1 ); /*Read the DTC header byte */
		
	elm_readdata( buf, 2 ); /*Read the DTC in 2 byte pairs until we get the one we want */
	cnt++; /*Increment the DTC byte pair counter*/
		
	if(cnt == 3) /*End of the current DTC line*/
	{
		cnt = 0;
	}	
	printf("Returning %x\n", buf[0]<<8| buf[1] );
	return buf[0]<<8| buf[1];

}
